
package DB;

import Data.*;

import java.sql.PreparedStatement;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

/**
 *Mapper Klasse, die Airline Objekte auf eine relationale DB abbildet.
 * Diese Klasse erlaubt zahlreiche Operationen, um eine Airline zu verwalten, z.B. eine LöschOperation oder eine EinfügeOperation
 * @author Stefan Fellner
 */
public class AirlineMapper {

    /*
     * Um sicher zu stellen, dass nur eine Instanz von der Klasse erzeugt wird, wird die Singleton Pattern angewendet.
     * Durch das Schlüsselwort static wird dies erreicht.
     * Quelle: http://www.theserverside.de/singleton-pattern-in-java/
     */
    private static AirlineMapper airlineMapper = null;

    private ConnectionManager manager;

    //mittels protected wird sichergestellt, dass mit new keine neuen Instanzen erstellt werden können. Sobals ein Mapper Objekt erstellt wird, wird eine connection aufgebaut
    protected AirlineMapper(){
        manager = ConnectionManager.getInstance();

    }

    /*Eine Instanz dieser Klasse sollte immer mittels der folgenden Methode erstellt werden.
     * Dadurch wird sicher gestellt, dass nur eine Instanz von dieser Klasse vorhanden ist.
     * @return DAS <code>AirlineMapper</code> Objekt
     * @see airlineMapper
     */
    public static AirlineMapper getInstance(){

        if(airlineMapper == null){
            airlineMapper = new AirlineMapper();
        }

        return airlineMapper;

    }


//Es folgen nun alle Methoden, die die Tabelle airline verändern können, bzw. deren inhalte abrufen können


    /*Eine Airline in die Datenbank einfügen.
     * @param das Objekt welches hinzugefügt werden soll
     * @return das eingefügte Objekt.
     * Diese Methode wird den Primärschlüssel automatisch erzeugt. Der größte vorhandene Schlüssel wird um 1 erhöht.
     */
    public Airline insert (Airline a){
        //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();


        /*Es wird sichergestellt, dass der name der einzufügenden Airline noch nicht in der Datenbank vorhanden ist.
        try{
            if(findByName(a.getAirName()) != null){
                //wenn schon eine Airline mit dem gleichen Namen existiert, wird null zurückgegeben und die insert operation findet für das übergebene Objekt nicht statt.
               return null;
            }
        }
        catch(RemoteException ex){
            ex.printStackTrace();
        }*/




        try{
            // Leeres SQL-Statement (JDBC) anlegen
            Statement stmt = con.createStatement();

            /*
             * Zunächst schauen wir nach, welches der momentan größte
             * Primärschlüsselwert einer Airline ist.
             */
            ResultSet maxId = stmt.executeQuery("SELECT MAX(airId) AS maxId " +"FROM airline ");

            //die ergebnis Tabelle muss eine maxId zurückgeben.
            if(maxId.next()){
                try{

                 //Das einzufügende Objekt a erhält den neu errechneten Primärschlüssel
                 a.setAirId(maxId.getInt("maxId") + 1);

                    stmt = con.createStatement();

                     /**
                     * Jetzt erst erfolgt die tatsächliche Einfügeoperation.
                     * Die Attribute der Airline, werden aus der übergebenen Airline mit den getter methoden geholt
                     */
                    stmt.executeUpdate("INSERT INTO airline (airId, airName)" +
                            "VALUES (" +
                            a.getAirId() + ", " + "'" +
                            a.getAirName() + "'" + ")");
                }
                catch(RemoteException ex){
                    ex.printStackTrace();
                }
            }

        }
        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }
        //dieses return ist notwendig, da sich die ID der Airline im Laufe der Methode verändert.
        return a;
    }







    /*Eine Airline updaten, d.h. die Attribute der Airline aktualisieren
     * Wenn der name der airline aktualisiert wird, muss überprüft werden, ob dieser Name schon existiert, wenn ja, ist ein update in dieser form nicht möglich
     * @param das Airline Objekt mit den neuen Attributen
     * @return das Airline Objekt, welches in der Datenbank gespeichert wurde
     */
     public Airline update (Airline a){

         //DB verbindung vom Connection Manager Objekt holen
        Connection con = this.manager.getConnection();


        //es wird sichergestellt, dass der name der einzufügenden Airline noch nicht in der Datenbank vorhanden ist.
        try{
            if(findByName(a.getAirName()) != null){
                //wenn schon eine Airline mit dem gleichen Namen existiert und dies nicht die Airline mit der übergeben ID ist, wird null zurückgegeben und die insert operation findet für das übergebene Objekt nicht statt.
                if(findByName(a.getAirName()).getAirId() != a.getAirId()){
                    return null;
                }
                //wenn schon ein Aircraft mit dem gleichen Namen existiert, wird null zurückgegeben und die insert operation findet für das übergebene Objekt nicht statt.
               return null;
            }
        }
        catch(RemoteException ex){
            ex.printStackTrace();
        }


        try{
                //sql statement
            PreparedStatement stmt = con.prepareStatement("UPDATE airline "+
                    "SET airName= "+"'"+a.getAirName()+"'"+
                    " WHERE airId = "+"'"+a.getAirId()+"'");
            //dieser integer beinhaltet die anzahl der veränderten datensätze nach einer bestimmten UPDATE operation
            int anzahlVeraenderterDatensaetze = stmt.executeUpdate();

            
            }
            //falls der Zugriff auf die Klasse Airline scheitert
            catch(RemoteException ex){
                ex.printStackTrace();
            }

        //falls die sql abfrage fehlschlägt...
        catch (SQLException e2) {
            e2.printStackTrace();
        }

        /*Das upgedatete airline Objekt wird zurückgegeben.
         *Dadurch wird sichergestellt, dass der Update funktioniert hat, da NULL bei fehlern zurückgegeben wird!*/
        return a;

     }


     /*Eine bestimmte Airline in der Datenbank löschen
      * @param die zu löschende Airline
      */
     public void delete (Airline a){

        //DB-Verbindung vom Connection Manager holen
        Connection con = this.manager.getConnection();

        try {
            // Leeres SQL-Statement anlegen
            Statement stmt = con.createStatement();

            stmt.executeUpdate("DELETE FROM airline "+
                               "WHERE airId = "+"'"+a.getAirId()+"'");
        }
        //falls der Zugriff auf die Klasse Airline fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
        }

     }

     /*Alle Airlines, die in der Tabelle vorhanden sind werden zurückgegeben
      *
      *@return Vector mit allen Airlines
      */
     public Vector<Airline> findAll(){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();
         //Vector, der alle Airline Objekte festhält
         Vector<Airline> allAirlines = new Vector<Airline>();

         try{

             Statement stmt = con.createStatement();
             //das ergebnis der abfrage in ein ResultSet "Tabelle" abspeichern. D.h. jedes tupel wird in eine Zeile des resultSets abgespeichert
             ResultSet allAirlinesQueryResult = stmt.executeQuery("SELECT * FROM airline "+"ORDER BY airId");

             //solange die ergebnisTabelle eine nachfolgende Zeile(Datensatz) hat, wird ein neues Airline Objekt erstellt und zum ergebnisVector hinzugefügt.
             while(allAirlinesQueryResult.next()){
                 //ein neues Airline Objekt anlegen, welches in den ergebnisvektor aufgenommen wird
                 Airline a = new AirlineImpl(allAirlinesQueryResult.getString("airName"));
                 a.setAirId(allAirlinesQueryResult.getInt("airId"));
                                             
                 //das gerade erstellte Objekt wird zum Ergebnisvektor hinzugefügt
                 allAirlines.addElement(a);
             }

         }
         catch(RemoteException ex){
             ex.printStackTrace();
         }
         catch(SQLException sqlE){
             sqlE.printStackTrace();
         }
         //der vector mit allen airlines aus der Tabelle wird zurückgegeben
         return allAirlines;
     }

     /*Airlines mit einem bestimmten Namen zurück geben. Da die Airlines nur 1 mal hinsichtlich des Namens vorkommen, kann 0 oder 1 Objekt zurückgegeben werden
      * @param den airlineNamen
      * @return Airline Objekt, dass dem übergebenen Namen entspricht.
      */

     public Airline findByName(String airName){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredAirline = stmt.executeQuery("SELECT * FROM airline "+
                                                         "WHERE airName = "+"'"+airName+"'");

             //prüfen, ob es eine Airline mit dem übergebenen Namen gibt
             if(desiredAirline.next()){
                 //ein neues airline Objekt anlegen
                 Airline a = new AirlineImpl(desiredAirline.getString("airName"));
                 //da der konstruktor das setzen der ID nicht zulässt, wird die ID über eine setter methode gesetzt.
                 a.setAirId(desiredAirline.getInt("airId"));
                 //das gerade erstellte Objekt wird zurückgegeben
                 return a;

            }

         }
         //falls der Zugriff auf die Klasse Airline fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //falls keine airline in der DB vorhanden ist und es keine Fehlermeldung gab, wird NULL zurückgegeben
         return null;
     }



     /*Einen bestimmten Airline abhängig von der ID finden
      *
      * @param die AirlineID als integer
      * @return das gesuchte Airline Objekt
      */

     public Airline findById(int airId){

         //DB-Verbindung vom Connection Manager holen
         Connection con = this.manager.getConnection();

         try{

             Statement stmt = con.createStatement();

             ResultSet desiredAirline = stmt.executeQuery("SELECT * FROM airline "+
                                                           "WHERE airId = "+"'"+airId+"'");

             //es gibt nur eine Airline mit gleicher ID. Es wird zunächst geschaut ob, ein Airline in der ResultSet "Tabelle" ist
             if(desiredAirline.next()){
                 //den ergebnisDatensatz in ein Objekt umwandeln
                 Airline a = new AirlineImpl(desiredAirline.getString("airName"));
                 a.setAirId(desiredAirline.getInt("airId"));
                 //die gefundene airline zurückgeben
                 return a;
             }

         }
         //falls der Zugriff auf die Klasse Airline fehlschlägt
        catch (RemoteException ex){
            ex.printStackTrace();
            return null;
        }
        //falls der sql Befehl fehlschlägt
        catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }
        //wenn keine Airline gefunden wurde und keine fehlermeldung ausgegeben wurde, gibt die methode null zurück.
         return null;
     }


}
